import copy

from django.template.loader import get_template
from django.template import Context
from django.core.urlresolvers import reverse
from django.core.exceptions import ImproperlyConfigured
from django.db.models.options import get_verbose_name
from django.utils.safestring import SafeUnicode

from modular import forms, loading, models

class ModuleBase(type):
    def __new__(cls, name, bases, attrs):
        # If this isn't a subclass of Module, don't do anything special.
        try:
            if not filter(lambda b: issubclass(b, Module), bases):
                return super(ModuleBase, cls).__new__(cls, name, bases, attrs)
        except NameError:
            # 'Model' isn't defined yet, meaning we're looking at Django's own
            # Model class, defined below.
            return super(ModuleBase, cls).__new__(cls, name, bases, attrs)

        # Make sure the model was specified properly
        if 'title' not in attrs:
            attrs['title'] = get_verbose_name(name).title()
        if 'template' not in attrs:
            raise ImproperlyConfigured, "%s must specify a template." % name

        # Set up a list of any form fields that were declared
        from django.newforms.forms import pretty_name
        attrs['_fields'] = {}
        for key, attr in attrs.items():
            if isinstance(attr, forms.Field):
                if not attr.label:
                    # Make sure the label remains intact in multi-module forms
                    attr.label = pretty_name(key)
                attrs['_fields'][key] = attrs.pop(key)

        # Create the class.
        module = type.__new__(cls, name, bases, attrs)

        # Register the class for future reference
        loading.registry.register(name, module)

        return module

class Module(object):
    __metaclass__ = ModuleBase
    is_default = False

    def __init__(self, user, id='', **kwargs):
        self.user = user
        self.id = id
        module_type = models.ModuleType.for_class(self.__class__)
        self._record = models.Module(user=user, type=module_type)

        for name, field in self._fields.items():
            if self.id:
                default = None
            else:
                default = field.initial
            setattr(self, name, kwargs.pop(name, default))

    def get_modules_for_user(cls, user):
        """
        Return a list of module instances that are valid for the given user.
        """
        defaults = loading.registry.get_defaults()

        if user.is_anonymous():
            # Anonymous users just get the default modules
            return [module(user) for module in defaults]

        # Add all modules the user has specified
        modules = []
        for record in user.module_set.all():
            try:
                module = Module._from_record(record)
            except KeyError:
                continue
            modules.append(module)
            defaults.discard(module.__class__)

        # Add any default modules the user has not yet specified
#        for module in defaults:
#            user.module_set.create(name=module.__name__)
        modules.extend(module(user) for module in defaults)

        return modules

    get_modules_for_user = classmethod(get_modules_for_user)

    get_all_modules = staticmethod(loading.registry.get_all_modules)

    def _from_id(id):
        return Module._from_record(models.Module.objects.get(pk=id))
    _from_id = staticmethod(_from_id)

    def _from_record(record):
        klass = loading.registry.get_module(record.name)
        module = klass(record.user, id=record._get_pk_val(), **record.preferences)
        module._record = record
        return module
    _from_record = staticmethod(_from_record)

    def _from_name(name):
        return loading.registry.get_module(name)
    _from_name = staticmethod(_from_name)

    def get_form(self):
        """
        Return a customized newforms Form for the specified module.
        """
        return forms.form_for_module(self)

    def is_customizable(self):
        """
        Determine whether the module has customization options.
        """
        return not self.user.is_anonymous() and len(self._fields) > 0

    def edit_url(self):
        if self.id:
            return reverse('modular.views.edit_module', kwargs={'module_id': self.id})
        return reverse('modular.views.add_module', kwargs={'module_type': self.__class__.__name__})
    edit_url = property(edit_url)

    def get_context(self):
        """
        Provide any additional context required by the module.
        This would be overridden when necessary.
        """
        return {}

    def render(self):
        """
        Render the module's template and return the rendered contents.
        """
        template = get_template(self.template)
        data = self.get_context()
        data.update(module=self, user=self.user)
        return template.render(Context(data))

    def __unicode__(self):
        return SafeUnicode(unicode(self.render()))
